﻿//-----------------------------------------------------------------------
// <copyright file="SetValue.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation.  All rights reserved.
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY 
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
// </copyright>
// <author>Steve Overall</author>
// <email>v-stevov</email>
// <date>2005-04-15</date>
// <summary>Writes the specified value to any elements or attributes matching the sepcified XPath.</summary>
//-----------------------------------------------------------------------

namespace Microsoft.Sdc.Tasks.XmlFile
{
	#region Using directives

	using System;
	using System.Collections.Generic;
	using System.Text;
	using System.Xml;

	using Microsoft.Build.Framework;
	using Microsoft.Build.Tasks;
	using Microsoft.Build.Utilities;

	using Microsoft.Sdc.Tasks;

	#endregion

	#region class comments
	/// <summary>
	/// Writes the specified value to the element(s) or attribute(s) that 
	/// match the passed XPath expression.  
	/// Will fail if no match found for the XPath expression.
	/// </summary>
	/// <remarks>
	/// <code>
	/// <![CDATA[<XmlFile.SetValue Path="path" XPath="xpath" Value="value" Namespaces="namespaces" IgnoreNoMatchFailure="ignoreNoMatchFailure" Force="force"/>]]>
	/// </code>
	/// <para>where:</para>
	/// <para><i>Path (Required)</i></para>
	/// <para>The path of the XML file to read.</para>
	/// <para><i>XPath (Required)</i></para>
	/// <para>The XPath expression to evaluate for the element(s) or attribute(s) whose value to set.</para>
	/// <para><i>Value</i></para>
	/// <para>The value to assign.</para>
	/// <para><i>Namespaces</i></para>
	/// <para>
	/// Optional TaskItems array detailing each of the namespace declarations required to read/maintain the target document.
	/// Each TaskItem should have 2 elements &lt;Prefix&gt; and &lt;Uri&gt;.
	/// </para>
	/// <para><i>IgnoreNoMatchFailure</i></para>
	/// <para>If set to true, no Exception will be thrown if no matches made for the supplied XPath.  Defaults to false.</para>
	/// <para><i>Force</i></para>
	/// <para>If set to true, this will force the file to be updated even if it is Read Only.  Defaults to false.</para>
	/// </remarks>
	/// <example>
	/// <code><![CDATA[
	/// <Project>
	///     <Target Name="Test" >
	///         <XmlFile.SetValue 
	///					Path="c:\app.config"
	///					XPath="/configuration/appSettings/add[@key='myKey']/@value"
	///					Value="my data value"
	///					Namespaces="@(ConfigNamespaces)"
	///					IgnoreNoMatchFailure="true"
	///					Force="true"
	///			/>
	///     </Target>
	/// </Project>
	/// ]]></code>    
	/// </example>
	#endregion
	public class SetValue : BaseXmlFileTask
	{
		#region private declarations
		private string		_value					= String.Empty;
		private bool		_ignoreNoMatchFailure	= false;
		private bool		_force					= false;
		#endregion

		#region constructors
		/// <summary>
		/// Create a new ChangeValue.
		/// </summary>
		public SetValue() : base()
		{
		}
		#endregion	

		#region override of InternalExecute
		/// <summary>
		/// Carry out this task's action.
		/// </summary>
		protected override void InternalExecute()
		{
			//	Load the XML file.
			XmlDocument xmlDoc = this.OpenXmlFile( this.Path );
			XmlNamespaceManager nsMgr = this.GetNamespaceManagerForDoc( xmlDoc, this.Namespaces );

			//	Run the XPath Expression against the XML file.
			//	Get the list of matching elements.
			XmlNodeList elements = this.SelectXmlNodesOfType( xmlDoc, this.XPath, XmlNodeType.Element, nsMgr );

			XmlNodeList nodes = xmlDoc.SelectNodes( this.XPath, nsMgr );
			if (nodes != null && nodes.Count > 0)
			{
				foreach (XmlNode node in nodes)
				{
					switch ( node.NodeType )
					{
						case XmlNodeType.Element:
							node.InnerText = _value;
							break;

						case XmlNodeType.Attribute:
							node.Value = _value;
							break;

						default:
							throw new TaskException( Log.FormatResourceString( "XmlFile.UnsupportedNodeType.Exception", node.NodeType ) );
					}
				}

				//	Save the Xml Doc back to disc.
				this.SaveXmlFile( xmlDoc, this.Path, _force );

				Log.LogMessageFromResources( "XmlFile.SetValue.Success", this.FileName, _value, elements.Count, this.XPath );
			}
			else
			{
				if ( _ignoreNoMatchFailure )
					Log.LogWarningFromResources( "XmlFile.NoMatch.Warning", this.FileName, this.XPath );
				else
					throw new TaskException( Log.FormatResourceString( "XmlFile.NoMatch.Exception", this.XPath ) );
			}
		}
		#endregion

		#region public Properties
		/// <summary>
		/// The new value to assign to the element or attribute.
		/// </summary>
		/// <value>The new value to assign to the element or attribute.</value>
		public string Value
		{
			get { return _value; }
			set { _value = ( value!=null ?  value : String.Empty ); }
		}

		/// <summary>
		/// If set to true, no Exception will be thrown if no matches made for the supplied XPath.  Defaults to false.
		/// </summary>
		/// <value>If set to true, no Exception will be thrown if no matches made for the supplied XPath.  Defaults to false.</value>
		public bool IgnoreNoMatchFailure
		{
			get { return _ignoreNoMatchFailure; }
			set { _ignoreNoMatchFailure = value; }
		}

		/// <summary>
		/// If set to true, this will modify the xml file even if it is read-only.  Defaults to false.
		/// </summary>
		/// <value>If set to true, this will modify the xml file even if it is read-only.  Defaults to false.</value>
		public bool Force
		{
			get { return _force; }
			set { _force = value; }
		}
		#endregion
	}
}
